This lets us compile and run examples using `cargo run --example NAME`.
Selecting the other binary targets is now done using the `--name` flag.
`cargo run` falls back to the old behaviour (running the only bin target
in the project, failing if there are more) in neither `--name` nor
`--example` are present.
Closes #538
use cargo::ops;
use cargo::core::{MultiShell};
-use cargo::util::{CliResult, CliError};
+use cargo::core::manifest::{BinTarget, ExampleTarget};
+use cargo::util::{CliResult, CliError, human};
use cargo::util::important_paths::{find_root_manifest_for_cwd};
#[deriving(Decodable)]
struct Options {
+ flag_name: Option<String>,
+ flag_example: Option<String>,
flag_jobs: Option<uint>,
flag_features: Vec<String>,
flag_no_default_features: bool,
Options:
-h, --help Print this message
+ --name NAME Name of the bin target to run
+ --example NAME Name of the example target to run
-j N, --jobs N The number of jobs to run in parallel
--release Build artifacts in release mode, with optimizations
--features FEATURES Space-separated list of features to also build
--manifest-path PATH Path to the manifest to execute
-v, --verbose Use verbose output
+If neither `--name` or `--example` are given, then if the project only has one
+bin target it will be run. Otherwise `--name` specifies the bin target to run,
+and `--example` specifies the example target to run. At most one of `--name` or
+`--example` can be provided.
+
All of the trailing arguments are passed as to the binary to run.
";
shell.set_verbose(options.flag_verbose);
let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path));
+ let env = if options.flag_example.is_some() {
+ "test"
+ } else if options.flag_release {
+ "release"
+ } else {
+ "compile"
+ };
+
let mut compile_opts = ops::CompileOptions {
- env: if options.flag_release { "release" } else { "compile" },
+ env: env,
shell: shell,
jobs: options.flag_jobs,
target: options.flag_target.as_ref().map(|t| t.as_slice()),
spec: None,
};
- let err = try!(ops::run(&root, &mut compile_opts,
+ let (target_kind, name) = match (options.flag_name, options.flag_example) {
+ (Some(bin), None) => (BinTarget, Some(bin)),
+ (None, Some(example)) => (ExampleTarget, Some(example)),
+ (None, None) => (BinTarget, None),
+ (Some(_), Some(_)) => return Err(CliError::from_boxed(
+ human("specify either `--name` or `--example`, not both"), 1)),
+ };
+
+ let err = try!(ops::run(&root,
+ target_kind,
+ name,
+ &mut compile_opts,
options.arg_args.as_slice()).map_err(|err| {
CliError::from_boxed(err, 101)
}));
}
}
}
-
use ops;
use util::{CargoResult, human, process, ProcessError, Require};
+use core::manifest::{TargetKind, LibTarget, BinTarget, ExampleTarget};
use core::source::Source;
use sources::PathSource;
pub fn run(manifest_path: &Path,
+ target_kind: TargetKind,
+ name: Option<String>,
options: &mut ops::CompileOptions,
args: &[String]) -> CargoResult<Option<ProcessError>> {
let mut src = try!(PathSource::for_path(&manifest_path.dir_path()));
let root = try!(src.get_root_package());
let env = options.env;
let mut bins = root.get_manifest().get_targets().iter().filter(|a| {
- a.is_bin() && a.get_profile().get_env() == env
+ let matches_kind = match target_kind {
+ BinTarget => a.is_bin(),
+ ExampleTarget => a.is_example(),
+ LibTarget(_) => false,
+ };
+ let matches_name = name.as_ref().map_or(true, |n| n.as_slice() == a.get_name());
+ matches_kind && matches_name && a.get_profile().get_env() == env
});
let bin = try!(bins.next().require(|| {
human("a bin target must be available for `cargo run`")
}));
match bins.next() {
- Some(..) => return Err(human("`cargo run` requires that a project only \
- have one executable")),
+ Some(..) => return Err(
+ human("`cargo run` requires that a project only have one executable. \
+ Use the `--name` option to specify which one to run")),
None => {}
}
let dst = manifest_path.dir_path().join("target");
let dst = match options.target {
Some(target) => dst.join(target),
- None => dst,
+ None => if bin.is_example() { dst.join("examples") } else { dst },
};
let exe = match bin.get_profile().get_dest() {
Some(s) => dst.join(s).join(bin.get_name()),
use std::path;
-use support::{project, execs, path2url};
+use support::{project, cargo_dir, execs, path2url};
use support::{COMPILING, RUNNING};
use hamcrest::{assert_that, existing_file};
assert_that(p.cargo_process("run"),
execs().with_status(101)
.with_stderr("`cargo run` requires that a project only \
- have one executable\n"));
+ have one executable. Use the `--name` option \
+ to specify which one to run\n"));
+})
+
+test!(specify_name {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/a.rs", r#"
+ extern crate foo;
+ fn main() { println!("hello a.rs"); }
+ "#)
+ .file("src/bin/b.rs", r#"
+ extern crate foo;
+ fn main() { println!("hello b.rs"); }
+ "#);
+
+ assert_that(p.cargo_process("run").arg("--name").arg("a"),
+ execs().with_status(0).with_stdout(format!("\
+{compiling} foo v0.0.1 ({dir})
+{running} `target{sep}a`
+hello a.rs
+",
+ compiling = COMPILING,
+ running = RUNNING,
+ dir = path2url(p.root()),
+ sep = path::SEP).as_slice()));
+
+ assert_that(p.process(cargo_dir().join("cargo")).arg("run").arg("--name").arg("b"),
+ execs().with_status(0).with_stdout(format!("\
+{running} `target{sep}b`
+hello b.rs
+",
+ running = RUNNING,
+ sep = path::SEP).as_slice()));
+})
+
+test!(run_example {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("examples/a.rs", r#"
+ fn main() { println!("example"); }
+ "#)
+ .file("src/bin/a.rs", r#"
+ fn main() { println!("bin"); }
+ "#);
+
+ assert_that(p.cargo_process("run").arg("--example").arg("a"),
+ execs().with_status(0).with_stdout(format!("\
+{compiling} foo v0.0.1 ({dir})
+{running} `target{sep}examples{sep}a`
+example
+",
+ compiling = COMPILING,
+ running = RUNNING,
+ dir = path2url(p.root()),
+ sep = path::SEP).as_slice()));
+})
+
+test!(either_name_or_example {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/bin/a.rs", r#"
+ fn main() { println!("hello a.rs"); }
+ "#)
+ .file("examples/b.rs", r#"
+ fn main() { println!("hello b.rs"); }
+ "#);
+
+ assert_that(p.cargo_process("run").arg("--name").arg("a").arg("--example").arg("b"),
+ execs().with_status(1)
+ .with_stderr("specify either `--name` or `--example`, \
+ not both"));
+})
+
+test!(one_bin_multiple_examples {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/main.rs", r#"
+ fn main() { println!("hello main.rs"); }
+ "#)
+ .file("examples/a.rs", r#"
+ fn main() { println!("hello a.rs"); }
+ "#)
+ .file("examples/b.rs", r#"
+ fn main() { println!("hello b.rs"); }
+ "#);
+
+ assert_that(p.cargo_process("run"),
+ execs().with_status(0).with_stdout(format!("\
+{compiling} foo v0.0.1 ({dir})
+{running} `target{sep}main`
+hello main.rs
+",
+ compiling = COMPILING,
+ running = RUNNING,
+ dir = path2url(p.root()),
+ sep = path::SEP).as_slice()));
})
test!(run_dylib_dep {